using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;
using System.Xml;
using System.Web;

namespace _18miljard.Db
{
    internal class QueryBuilder : XmlDocument
    {
		StringBuilder queryString;
		ResultType resultType;
		ResultSet resultSet = new ResultSet();
		MiljardDb db = MiljardDb.Miljard;
		System.Data.CommandType commandType = System.Data.CommandType.Text;

		string expireCache = null;

		internal QueryBuilder(string name)
		{
			this.ReadQuery(name);
		}

		internal StringBuilder QueryString
		{
			get { return this.queryString; }
		}

		internal string ExpireCache
		{
			get { return this.expireCache; }
		}

		internal ResultType ResultType
		{
			get { return this.resultType; }
		}

		internal System.Data.CommandType CommandType
		{
			get { return this.commandType; }
		}

		internal ResultSet ResultSet
		{
			get { return this.resultSet; }
		}

		internal MiljardDb Db
		{
			get { return this.db; }
		}

		private void ReadQuery(string name)
		{
			string path = HttpContext.Current.Server.MapPath("/App_Data/DBQueries.xml");

			this.Load(path);

			XmlNode node = this.SelectSingleNode("/miljard/queries/query[@name='" + name + "']");

			if (node != null)
			{
				XmlAttribute attr = node.Attributes["expirecache"];
				if (attr != null)
					this.expireCache = attr.Value;

				this.SetDb(node);

				this.SetResultType(node);

				//this.queryString = new StringBuilder(node.Attributes["command"].Value);
				this.ParseQueryType(node);
			}
			else
				throw new ArgumentException("Unknown query: " + name);

			this.RemoveAll();
		}

		private void SetDb(XmlNode node)
		{
			XmlAttribute attr = node.Attributes["db"];
			if (attr != null)
			{
				switch (attr.Value)
				{
					case "mail": this.db = MiljardDb.MiljardMail;
						break;
					case "ad": this.db = MiljardDb.MiljardAd;
						break;
				}
			}
		}

		private void SetResultType(XmlNode node)
		{
			string resultType = node.Attributes["resulttype"].Value;

			switch (resultType)
			{
				case "singlerow": this.resultType = ResultType.SingleRow;
					break;
				case "multirow": this.resultType = ResultType.MultiRow;
					break;
				case "scalar": this.resultType = ResultType.Scalar;
					break;
				case "nonquery": this.resultType = ResultType.NonQuery;
					break;
				case "identity": this.resultType = ResultType.Identity;
					break;

				default: throw new ArgumentException("Invalid resulttype: " + resultType);
			}
		}

		private void ParseQueryType(XmlNode node)
		{
			string leftSide = node.Attributes["type"].Value;

			switch (leftSide)
			{
				case "select": this.queryString = this.GetSelect(node);
					break;
				case "delete": this.queryString = this.GetDelete(node);
					break;
				case "insert": this.queryString = this.GetInsert(node);
					break;
				case "update": this.queryString = this.GetUpdate(node);
					break;
				case "storedprocedure":
					this.queryString = this.GetStoredProcedure(node);
					this.commandType = System.Data.CommandType.StoredProcedure;
					break;
				default: throw new ArgumentException("Invalid query type: " + leftSide);
			}
		}

		private StringBuilder GetSelect(XmlNode node)
		{
			StringBuilder query = new StringBuilder("SELECT ", 128);

			XmlAttribute attr = node.Attributes["predicate"];

			if (attr != null)
			{
				query.Append(" ");
				query.Append(attr.Value);
				query.Append(" ");
			}

			bool first = true;

			foreach (XmlNode childNode in node.ChildNodes)
			{
				switch (childNode.LocalName)
				{
					case "column":
						if (!first)
							query.Append(", ");
						else
							first = false;

						query.Append(this.ParseSelectColumn(childNode));
						break;
				}
			}

			query.Append(" FROM ");
			query.Append(node.Attributes["tables"].Value);

			attr = node.Attributes["where"];

			if (attr != null)
			{
				query.Append(" WHERE ");
				query.Append(attr.Value);
			}

			attr = node.Attributes["groupby"];
			if (attr != null)
			{
				query.Append(" GROUP BY ");
				query.Append(attr.Value);
			}

			attr = node.Attributes["order"];
			if (attr != null)
			{
				query.Append(" ORDER BY ");
				query.Append(attr.Value);
			}

			return query;
		}

		private string ParseSelectColumn(XmlNode node)
		{
			XmlAttribute attr = node.Attributes["alias"];

			string columnName = node.Attributes["name"].Value;

			string resultSetColumn = (attr == null ? node.Attributes["name"].Value : attr.Value);

			this.resultSet.Columns.Add(resultSetColumn);

			StringBuilder selectColumn = new StringBuilder(columnName);

			if (attr != null)
			{
				selectColumn.Append(" AS ");
				selectColumn.Append(attr.Value);
			}

			return selectColumn.ToString();
		}

		private StringBuilder GetDelete(XmlNode node)
		{
			StringBuilder delete = new StringBuilder("DELETE FROM ", 128);

			delete.Append(node.Attributes["table"].Value);

			XmlAttribute attr = node.Attributes["where"];

			if (attr != null)
			{
				delete.Append(" WHERE ");
				delete.Append(attr.Value);
			}

			return delete;
		}

		private StringBuilder GetInsert(XmlNode node)
		{
			StringBuilder query = new StringBuilder("INSERT INTO ", 128);

			query.Append(node.Attributes["table"].Value);

			query.Append(" (");

			query.Append(node.Attributes["columns"].Value);

			query.Append(") VALUES(");

			query.Append(node.Attributes["values"].Value);

			query.Append(")");

			return query;
		}

		private StringBuilder GetUpdate(XmlNode node)
		{
			StringBuilder query = new StringBuilder("UPDATE ", 128);

			query.Append(node.Attributes["table"].Value);

			query.Append(" ");

			bool first = true;

			foreach (XmlNode childNode in node.ChildNodes)
			{
				switch (childNode.LocalName)
				{
					case "column":
						if (first)
						{
							first = false;
							query.Append("SET ");
						}
						else
							query.Append(", ");

						query.Append(childNode.Attributes["name"].Value);
						query.Append(" = ");
						query.Append(childNode.Attributes["value"].Value);
						break;
				}
			}

			XmlAttribute attr = node.Attributes["where"];

			if (attr != null)
			{
				query.Append(" WHERE ");
				query.Append(attr.Value);
			}

			return query;
		}

		private StringBuilder GetStoredProcedure(XmlNode node)
		{
			StringBuilder query = new StringBuilder(node.Attributes["spname"].Value);

			return query;
		}
		
    }
}
